package com.bdf.congcache.core.memory.shrinking;

import com.bdf.congcache.core.control.ContextCache;
import com.bdf.congcache.core.control.event.ElementEventType;
import com.bdf.congcache.core.memory.IMemoryCache;
import com.bdf.congcache.core.model.ICacheElement;
import com.bdf.congcache.core.model.IElementAttributes;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.util.Set;

/**
 * @Author 田培融
 * @Description 压缩内存的线程类
 * @Date 8:12 2019/7/11
 **/
public class ShrinkingThread<K,V> implements Runnable {
    private static final Log log = LogFactory.getLog(ShrinkingThread.class);

    private final ContextCache<K,V> cache;

    private final long maxMemoryIdleTime;

    private final int maxSpoolPerRun;

    private boolean spoolLimit = false;


    /**
     * @Author 田培融
     * @Description  根据要压缩的缓存对象进行构造，
     *                  如果缓存的最大空闲时间小于则maxMemoryIdleTime = -1 ，否则为缓存设置（秒）时间的1000倍，
     *                  缓存对像可以清理线程池进行限制， 如果maxPoolPerRun !=-1  则进行限制 this.poolLimit = true
     * @Date 8:22 2019/7/11
     **/
    public ShrinkingThread(ContextCache<K, V> cache) {
        super();
        this.cache = cache;
        long maxMemoryIdleTimeSeconds = cache.getCacheAttributes().getMaxMemoryIdleTimeSeconds();

        if (maxMemoryIdleTimeSeconds < 0){
            this.maxMemoryIdleTime = -1;
        }else {
            this.maxMemoryIdleTime = maxMemoryIdleTimeSeconds*1000;
        }

        this.maxSpoolPerRun =cache.getCacheAttributes().getMaxSpoolPerRun();
        if (this.maxSpoolPerRun!= -1){
            this.spoolLimit = true;
        }

    }

    @Override
    public void run() {
        shrink();
    }
    protected void shrink(){
        if (log.isDebugEnabled()){
            log.debug(" Shrink memory cache for : " +cache.getCacheName());
        }

        IMemoryCache<K,V> memCache= cache.getMemoryCache();

        try {
            Set<K> keys = memCache.getKeySet();
            int size = keys.size();
            if (log.isDebugEnabled()){
                log.debug("Keys size:" +size);
            }

            ICacheElement<K,V> cacheElement;
            IElementAttributes attributes;

            int spoolCount = 0 ;

            for (K key : keys) {
                cacheElement = memCache.getQuiet(key);
                if (cacheElement == null){
                    continue;
                }
                attributes= cacheElement.getElementAttributes();
                boolean remove = false;
                long now = System.currentTimeMillis();
                // 判断是否不清空
                if (!attributes.getIsEternal()){
                     remove = cache.isExpired(cacheElement, now, ElementEventType.EXCEEDED_MAXLIFE_BACKGROUND,
                            ElementEventType.EXCEEDED_IDLETIME_BACKGROUND);

                     if (remove){
                        memCache.remove(cacheElement.getKey());
                     }
                }


                // 空间时间还没有过期
                if (!remove && maxMemoryIdleTime !=-1 ){

                    if (!spoolLimit || spoolCount <this.maxSpoolPerRun){

                        // 最后访问的时间
                        final long lastAccessTime = attributes.getLastAccessTime();

                        if (lastAccessTime +maxMemoryIdleTime <now){
                            if (log.isDebugEnabled()){
                                log.debug("Exceed memory idle time : "+cacheElement.getKey());
                            }

                            spoolCount++;
                            memCache.remove(cacheElement.getKey());
                            memCache.waterfal(cacheElement);
                            key = null;
                            cacheElement = null;
                        }

                    }else {
                        if (log.isDebugEnabled()){
                            log.debug("spoolCount = '" + spoolCount + "'; " + "maxSpoolPerRun = '" + maxSpoolPerRun
                                    + "'");
                        }
                        //todo  此处不知什么意思
                        if (spoolLimit && spoolCount >= this.maxSpoolPerRun)
                        {
                            return;
                        }
                    }
                }

            }
        }catch (Throwable e ){
            log.info("Error occur in shrink", e);
            return;
        }
    }

}
